require "trifle/ruby/app_job"
require "trifle/ruby/app_exception"
require "trifle/ruby/wrappers/exception_payload"

module Trifle
  module Ruby
    module Middleware
      class SidekiqJob

        def call(worker, job, queue)
          environment = ::Rails.env || ENV["rack_env"] || ENV["RAILS_ENV"]
          config = Trifle::Ruby::Config.load[environment]
          if ( config["sidekiq"]["ignore"]["queues"].include?( queue.to_s ) rescue false )
            yield
          else
            name = "jobs.sidekiq"
            monitor_job(
              name,
              location:    "#{name}##{job['class']}",
              method:      'perform',
              url:         job['class'],
              metadata:    clean_meta(job),
              params:      clean_params(job['args']),
              queue_start: job['enqueued_at']
            ) do
              yield
            end
          end
        end

        def monitor_job(name, payload={})
          begin
            job = AppJob.new(SecureRandom.uuid, payload[:location])
            job.current!
            ActiveSupport::Notifications.instrument("#{name}.start_processing", payload)
            ActiveSupport::Notifications.instrument("#{name}.end_processing", payload) do
              yield
            end
          rescue Exception => e
            environment = ::Rails.env || ENV["rack_env"] || ENV["RAILS_ENV"]
            exception_payload = ExceptionPayload.new( ENV, e,
              {
                "environment" => environment,
                "location" => payload[:location],
                "session" => {},
                "params" => payload[:params],
                "url" => payload[:url],
                "user_agent" => "",
                "reference_type" => "job",
                "reference_id" => job.request_id
              }
            )
            AppException.new(exception_payload.generate!).complete!
            raise e
          ensure
            job.complete!
          end
        end

        def filtered_keys
          @filtered_keys ||= Set.new(%w(class args retried_at failed_at
            error_message error_class backtrace error_backtrace enqueued_at retry
          ))
        end

        def clean_meta(job)
          job.except(*filtered_keys).map do |key, value|
            value.inspect.truncate(100)
          end
        end

        def clean_params(params)
          params.map do |param|
            param.inspect.truncate(100)
          end
        end
      end
    end
  end
end
